package tuanda.soict.firstsun.LogicPackage;

import java.util.ArrayList;
import java.util.Random;

import tuanda.soict.firstsun.AbilityPackage.Ability;
import tuanda.soict.firstsun.BuildingPackage.Army_Building;
import tuanda.soict.firstsun.BuildingPackage.Building;
import tuanda.soict.firstsun.BuildingPackage.Gold_Building;
import tuanda.soict.firstsun.BuildingPackage.HP_Building;
import tuanda.soict.firstsun.BuildingPackage.HQ_Building;
import tuanda.soict.firstsun.DataPackage.ArmyDataSet;
import tuanda.soict.firstsun.DataPackage.BuildingDataSet;
import tuanda.soict.firstsun.DataPackage.CommonConstants;

/**
 * @author tuanda_soict_hut
 * @version 0.0.5
 * @since 17-10-2012
 */
public abstract class Scenario implements CommonConstants {

	protected BattleMap map;
	protected Group[] group;
	protected int[] turn;
	protected int currentTurn;
	protected ArrayList<Building> neutralBuilding;
	protected int[] playerType;

	public Scenario(BattleMap batMap, Group[] gro, int firstTurn,
			int[] playType, ArrayList<Building> neutral) {
		Group.resetGroupNumber();
		setBattleMap(batMap);
		setGroup(gro);
		setTurn(gro.length);
		setPlayerType(playType);
		setFirstTurn(firstTurn);
		setNeutralBuilding(neutral);
	}

	public final boolean isLost(int groupID) {
		if (groupID > -1 && groupID < group.length) {
			if (group[groupID] != null) {
				if (group[groupID].isLost() == true) {
					return true;
				} else if (group[groupID].getHQBuilding().getTurnsBeforeLost() == 0) {
					return true;
				} else if (group[groupID].getNumberArmy() == 0) {
					Building[] status = group[groupID].getBuilding();
					for (int i = 0; i < status.length; i++) {
						if (status[i] != null) {
							if (status[i].getBuildingType() == PRODUCE_ARMY_TYPE) {
								return false;
							}
						}
					}
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public final void updateLost() {
		for (int i = 0; i < group.length; i++) {
			if (group[i] != null) {
				if (isLost(i) == true) {
					group[i].setLost(true);
					removeLostGroup(i);
				}
			}
		}
	}

	public final Group findWinGroup() {
		for (int i = 0; i < group.length; i++) {
			if (group[i] != null) {
				if (isWin(i)) {
					return group[i];
				}
			}
		}
		return null;
	}

	public final boolean isWin(int groupID) {
		if (groupID > -1 && groupID < group.length) {
			for (int i = 0; i < group.length; i++) {
				if (i != groupID) {
					if (group[i] != null) {
						if (this.isLost(i) == false) {
							return false;
						}
					}
				}
			}
			return true;
		} else {
			return false;
		}
	}

	public final void nextTurn() {
		postMainPhase();
		this.currentTurn = (currentTurn + 1) % (this.group.length);
		while (group[currentTurn] == null) {
			this.currentTurn = (currentTurn + 1) % (this.group.length);
		}
		preMainPhase();
	}

	public final int getCurrentTurn() {
		return this.currentTurn;
	}

	public final void setFirstTurn(int firstTurn) {
		if (firstTurn > -1 && firstTurn < turn.length) {
			this.currentTurn = firstTurn;
		} else {
			this.currentTurn = 0;
		}
	}

	public final int getTurnWithID(int id) {
		if (id > -1 && id < group.length) {
			return turn[id];
		} else {
			return GROUP_NOT_EXIST;
		}
	}

	public final void setTurn(int length) {
		this.turn = new int[length];
		for (int i = 0; i < turn.length; i++) {
			this.turn[i] = -1;
		}
	}

	public final void setBattleMap(BattleMap batMap) {
		this.map = batMap;
	}

	public final BattleMap getBattleMap() {
		return this.map;
	}

	public final void setGroup(Group[] gro) {
		this.group = gro;
	}

	public final Group[] getGroup() {
		return this.group;
	}

	public final Group getGroup(int id) {
		if (id > -1 && id < group.length) {
			return group[id];
		} else {
			return null;
		}
	}

	public final boolean isInSameSide(int groupID_1, int groupID_2) {
		if (groupID_1 > -1 && groupID_1 < DEFAULT_MAX_GROUP) {
			if (groupID_2 > -1 && groupID_2 < DEFAULT_MAX_GROUP) {
				if (group[groupID_1].getSide() == group[groupID_2].getSide()) {
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public final int getPlayerType(int id) {
		if (id > -1 && id < playerType.length) {
			return this.playerType[id];
		} else {
			return HUMAN_PLAYER;
		}
	}

	public final int[] getPlayerType() {
		return playerType;
	}

	public final void setPlayerType(int[] play_type) {
		this.playerType = play_type;
	}

	public final ArrayList<Building> getNeutralBuilding() {
		return neutralBuilding;
	}

	public final void setNeutralBuilding(ArrayList<Building> neutralBuild) {
		this.neutralBuilding = neutralBuild;
	}

	public final void initializeArmyDefense() {
		Army[] tempArmy;
		for (int i = 0; i < group.length; i++) {
			if (group[i] != null) {
				tempArmy = group[i].getArmy();
				for (int j = 0; j < tempArmy.length; j++) {
					if (tempArmy[j] != null) {
						int x = tempArmy[j].getLocationX();
						int y = tempArmy[j].getLocationY();
						if (map.getBuilding(x, y) != null) {
							tempArmy[j].setTempDefense(map.getBuilding(x, y)
									.getDefense());
						}
						tempArmy[j].setTempDefense(tempArmy[j].getTempDefense()
								+ map.getEnvironment(x, y).getDefense());
					}
				}
			}
		}
	}

	public final Building addNeutralBuildingToMap(BuildingDataSet build, int x,
			int y) {
		int category = build.getCategory();
		Building result;
		switch (category) {
		case UNKNOWN_TYPE:
			result = new Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getType(), x, y, build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case PRODUCE_GOLD_TYPE:
			result = new Gold_Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getType(), build.getGoldProduce(), x, y,
					build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case PRODUCE_ARMY_TYPE:
			result = new Army_Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getType(), build.getGoldProduce(),
					build.getHPRestore(), build.getArmyProduceType(), x, y,
					build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case RESTORE_HP_TYPE:
			result = new HP_Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getType(), build.getGoldProduce(),
					build.getHPRestore(), x, y, build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case HEAD_QUARTER_TYPE:
			result = new HQ_Building(build.getName(), build.getDefense(),
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getType(), build.getGoldProduce(),
					build.getHPRestore(), x, y, build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		default:
			result = new Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getType(), x, y, build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		}
	}

	public final Building addNeutralBuildingToMap(Building build, int x, int y) {
		int category = build.getBuildingType();
		Building result;
		switch (category) {
		case UNKNOWN_TYPE:
			result = new Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getUnitType(), x, y, build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case PRODUCE_GOLD_TYPE:
			result = new Gold_Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getUnitType(),
					((Gold_Building) build).getGoldProduce(), x, y,
					build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case PRODUCE_ARMY_TYPE:
			result = new Army_Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getUnitType(),
					((Army_Building) build).getGoldProduce(),
					((Army_Building) build).getHPRestore(),
					((Army_Building) build).getArmyProduceType(), x, y,
					build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case RESTORE_HP_TYPE:
			result = new HP_Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getUnitType(),
					((HP_Building) build).getGoldProduce(),
					((HP_Building) build).getHPRestore(), x, y,
					build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		case HEAD_QUARTER_TYPE:
			result = new HQ_Building(build.getName(), build.getDefense(),
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getUnitType(),
					((HQ_Building) build).getGoldProduce(),
					((HQ_Building) build).getHPRestore(), x, y,
					build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		default:
			result = new Building(build.getName(), build.getDefense(), 0,
					DEFAULT_MAX_GROUP, build.getObstaclePoint(),
					build.getUnitType(), x, y, build.getDataID());
			neutralBuilding.add(result);
			map.setBuilding(result, x, y);
			return result;
		}
	}

	public final Army addInitialArmy(ArmyDataSet army, int groupID, int x, int y) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Ability[] ability = new Ability[MAX_NUMBER_SKILL];
			for (int p = 0; p < MAX_NUMBER_SKILL; p++) {
				ability[p] = Ability.abilityInitializer(army.getSkill(p));
			}
			Army arm = group[groupID].addInitialArmy(army.getName(),
					army.getRank(), army.getType(), army.getMinAttack(),
					army.getMaxAttack(), army.getDefense(), army.getMovement(),
					army.getAttackRange(), army.getGoldRequire(), x, y,
					army.getDataID(), ability,
					((new Random()).nextInt(NUMBER_ATTRIBUTE)));
			map.setArmy(arm);
			return arm;
		} else {
			return null;
		}
	}

	public final boolean isEnoughGold(int gold, int groupID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			if (group[groupID].getIncome() < gold) {
				return false;
			} else {
				return true;
			}
		} else {
			return false;
		}
	}

	public final Army addNewArmy(String nam, int groupID, int ran, int typ,
			int minAtk, int maxAtk, int def, int mov, int range, int gold,
			int x, int y, int dataID, Ability[] ability, int attr) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Army army = group[groupID].addArmy(nam, ran, typ, minAtk, maxAtk,
					def, mov, range, gold, x, y, dataID, ability, attr);
			army.setTempDefense(map.getEnvironment(x, y).getDefense());
			Building build = map.getBuilding(x, y);
			if (build != null) {
				army.setTempDefense(army.getTempDefense() + build.getDefense());
			}
			map.setArmy(army);
			return army;
		} else {
			return null;
		}
	}

	public final Army addNewArmy(ArmyDataSet army, int groupID, int x, int y,
			int attr) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Ability[] ability = new Ability[MAX_NUMBER_SKILL];
			for (int p = 0; p < MAX_NUMBER_SKILL; p++) {
				ability[p] = Ability.abilityInitializer(army.getSkill(p));
			}
			Army arm = group[groupID].addArmy(army.getName(), army.getRank(),
					army.getType(), army.getMinAttack(), army.getMaxAttack(),
					army.getDefense(), army.getMovement(),
					army.getAttackRange(), army.getGoldRequire(), x, y,
					army.getDataID(), ability, attr);
			arm.setTempDefense(map.getEnvironment(x, y).getDefense());
			Building build = map.getBuilding(x, y);
			if (build != null) {
				arm.setTempDefense(arm.getTempDefense() + build.getDefense());
			}
			map.setArmy(arm);
			return arm;
		} else {
			return null;
		}
	}

	public final Building addBuilding(BuildingDataSet build, int groupID,
			int x, int y) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			int category = build.getCategory();
			Building building;
			switch (category) {
			case UNKNOWN_TYPE:
				building = group[groupID].addBuilding(build.getName(),
						build.getDefense(), build.getObstaclePoint(),
						build.getType(), x, y, build.getDataID());
				map.setBuilding(building);
				return building;
			case PRODUCE_GOLD_TYPE:
				building = group[groupID].addGoldBuilding(build.getName(),
						build.getDefense(), build.getObstaclePoint(),
						build.getType(), build.getGoldProduce(), x, y,
						build.getDataID());
				map.setBuilding(building);
				return building;
			case PRODUCE_ARMY_TYPE:
				building = group[groupID].addArmyBuilding(build.getName(),
						build.getDefense(), build.getObstaclePoint(),
						build.getType(), build.getGoldProduce(),
						build.getHPRestore(), build.getArmyProduceType(), x, y,
						build.getDataID());
				map.setBuilding(building);
				return building;
			case RESTORE_HP_TYPE:
				building = group[groupID].addHPBuilding(build.getName(),
						build.getDefense(), build.getObstaclePoint(),
						build.getType(), build.getGoldProduce(),
						build.getHPRestore(), x, y, build.getDataID());
				map.setBuilding(building);
				return building;
			case HEAD_QUARTER_TYPE:
				building = group[groupID].setHqBuilding(new HQ_Building(build
						.getName(), build.getDefense(), groupID, build
						.getObstaclePoint(), build.getType(), build
						.getGoldProduce(), build.getHPRestore(), x, y, build
						.getDataID()));
				map.setBuilding(building);
				return building;
			default:
				building = group[groupID].addBuilding(build.getName(),
						build.getDefense(), build.getObstaclePoint(),
						build.getType(), x, y, build.getDataID());
				map.setBuilding(building);
				return building;
			}
		} else {
			return null;
		}
	}

	public final Building addHQBuilding(String nam, int groupID, int def,
			int obsPoint, int typ, int goldProd, int hp, int x, int y,
			int dataID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Building building = group[groupID].setHqBuilding(new HQ_Building(
					nam, def, groupID, obsPoint, typ, goldProd, hp, x, y,
					dataID));
			map.setBuilding(building);
			return building;
		} else {
			return null;
		}
	}

	public final Building addNewBuilding(String nam, int groupID, int def,
			int obsPoint, int typ, int x, int y, int dataID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Building building = group[groupID].addBuilding(nam, def, obsPoint,
					typ, x, y, dataID);
			map.setBuilding(building);
			return building;
		} else {
			return null;
		}
	}

	public final Building addGoldBuilding(String nam, int groupID, int def,
			int obsPoint, int typ, int goldProd, int x, int y, int dataID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Building building = group[groupID].addGoldBuilding(nam, def,
					obsPoint, typ, goldProd, x, y, dataID);
			map.setBuilding(building);
			return building;
		} else {
			return null;
		}
	}

	public final Building addHPBuilding(String nam, int groupID, int def,
			int obsPoint, int typ, int goldProd, int hp, int x, int y,
			int dataID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Building building = group[groupID].addHPBuilding(nam, def,
					obsPoint, typ, goldProd, hp, x, y, dataID);
			map.setBuilding(building);
			return building;
		} else {
			return null;
		}
	}

	public final Building addArmyBuilding(String nam, int groupID, int def,
			int obsPoint, int typ, int goldProd, int hp, int type, int x,
			int y, int dataID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Building building = group[groupID].addArmyBuilding(nam, def,
					obsPoint, typ, goldProd, hp, type, x, y, dataID);
			map.setBuilding(building);
			return building;
		} else {
			return null;
		}
	}

	public final void removeHQBuilding(int groupID, int x, int y) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			if (this.group[groupID] != null) {
				this.group[groupID].removeHQBuilding();
				this.map.setBuilding(null, x, y);
			}
		}
	}

	public final void removeArmy(int id, int groupID, int x, int y) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			if (this.group[groupID] != null) {
				this.group[groupID].removeArmy(id);
				this.map.setArmy(null, x, y);
			}
		}
	}

	public final void removeBuilding(int id, int groupID, int x, int y) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			if (this.group[groupID] != null) {
				this.group[groupID].removeBuilding(id);
				this.map.setBuilding(null, x, y);
			}
		}
	}

	public final void removeNeutralBuilding(Building build) {
		for (int i = 0; i < neutralBuilding.size(); i++) {
			if (neutralBuilding.get(i) == build) {
				neutralBuilding.remove(i);
			}
		}
	}

	public final void initialzeEnemyHQ() {
		Group currentGroup;
		for (int i = 0; i < getGroup().length; i++) {
			currentGroup = getGroup(i);
			currentGroup.getStrategyInfomation().setEnemyHQLocation(
					findNearestEnemyHQLocation(currentGroup));
		}
		preMainPhase();
	}

	public final Coordinate findNearestEnemyHQLocation(Group currentGroup) {
		if (currentGroup != null) {
			Coordinate origin = currentGroup.getHQBuilding().getCoordinate();
			Coordinate coor = null;
			int distance = Integer.MAX_VALUE;
			int temp;
			int side = currentGroup.getSide();
			if (currentGroup.getHQBuilding().getTurnsBeforeLost() != MANY_TURNS) {
				return origin;
			} else {
				for (int i = 0; i < getGroup().length; i++) {
					if (getGroup(i).getSide() != side) {
						temp = origin.getDistance(getGroup(i).getHQBuilding()
								.getCoordinate());
						if (distance > temp) {
							distance = temp;
							coor = getGroup(i).getHQBuilding().getCoordinate();
						}
					}
				}
				return coor;
			}
		} else {
			return null;
		}
	}

	public final void moveTo(Army arm, Coordinate destination) {
		if (arm != null && destination != null) {
			int x = arm.getLocationX();
			int y = arm.getLocationY();

			int dx = destination.getLocationX();
			int dy = destination.getLocationY();

			map.setArmy(null, x, y);
			arm.setTempDefense(arm.getTempDefense()
					- this.getTotalMapObjectDefense(x, y));

			arm.setCoordinate(destination);

			map.setArmy(arm);
			arm.setTempDefense(arm.getTempDefense()
					+ this.getTotalMapObjectDefense(dx, dy));
			arm.setMoveLeft(false);
		}
	}

	public final int getTotalMapObjectDefense(int x, int y) {
		Building currentBuilding = map.getBuilding(x, y);
		if (currentBuilding != null) {
			return (map.getEnvironment(x, y).getDefense() + currentBuilding
					.getDefense());
		} else {
			return map.getEnvironment(x, y).getDefense();
		}
	}

	public abstract void endScenario();

	public final void removeLostGroup(int groupID) {
		if (groupID > -1 && groupID < DEFAULT_MAX_GROUP) {
			Army[] armyGroup = group[groupID].getArmy();
			Building[] buildingGroup = group[groupID].getBuilding();
			HQ_Building hq = group[groupID].getHQBuilding();
			removeHQBuilding(groupID, hq.getLocationX(), hq.getLocationY());
			int i;
			for (i = 0; i < armyGroup.length; i++) {
				if (armyGroup[i] != null) {
					removeArmy(i, groupID, armyGroup[i].getLocationX(),
							armyGroup[i].getLocationY());
				}
			}
			for (i = 0; i < buildingGroup.length; i++) {
				if (buildingGroup[i] != null) {
					addNeutralBuildingToMap(buildingGroup[i],
							buildingGroup[i].getLocationX(),
							buildingGroup[i].getLocationY());
					group[groupID].removeBuilding(i);
				}
			}
			group[groupID] = null;
		}
	}

	public final void preMainPhase() {
		if (this.getGroup(currentTurn).isLost() == true) {
			this.nextTurn();
		} else if (isLost(this.getCurrentTurn()) == true) {
			this.getGroup(currentTurn).setLost(true);
			this.nextTurn();
		} else if (isWin(this.getCurrentTurn()) == true) {
			this.endScenario();
		} else {
			this.getGroup(currentTurn).preMainPhase(map);
		}
		Army[] arm = group[currentTurn].getArmy();
		for (int i = 0; i < arm.length; i++) {
			if (arm[i] != null) {
				arm[i].setActionLeft(true);
				arm[i].setMoveLeft(true);
			}
		}
		turn[currentTurn]++;
	}

	public final void postMainPhase() {
		if (isWin(this.getCurrentTurn()) == true) {
			this.endScenario();
		} else {
			this.getGroup(currentTurn).postMainPhase();
		}

		Army[] armyStatusTemp;
		for (int j = 0; j < group.length; j++) {
			if (group[j] != null) {
				armyStatusTemp = group[j].getArmy();
				for (int i = 0; i < armyStatusTemp.length; i++) {
					if (armyStatusTemp[i] != null) {
						armyStatusTemp[i].decreaseEffectDuration();
					}
				}
			}
		}
	}
}
